package cn.yangliu.nacos.gateway.service.impl;

import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static cn.yangliu.nacos.gateway.constants.CacheConstants.CLIENT_TOKEN_INFO_CACHE;
import static cn.yangliu.nacos.gateway.constants.CacheConstants.GATEWAY_AUTHORITY_CACHE;

import cn.yangliu.nacos.comm.JsonResult;
import cn.yangliu.nacos.comm.tools.JsonUtils;
import cn.yangliu.nacos.comm.tools.ResponseUtils;
import cn.yangliu.nacos.gateway.entity.Oauth2ClientAuthority;
import cn.yangliu.nacos.gateway.model.Oauth2TokenInfo;
import cn.yangliu.nacos.gateway.remote.Oauth2Service;
import cn.yangliu.nacos.gateway.repository.Oauth2ClientAuthorityRepository;
import cn.yangliu.nacos.gateway.service.Oauth2ClientService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * The type of nacos.
 *
 * @author 问道于盲
 * @date 2020 -01-01
 */
@Service
@Transactional(readOnly = true)
public class Oauth2ClientServiceImpl implements Oauth2ClientService {

    /**
     * current class instance's member.
     * The Repository.
     */
    @Autowired
    private Oauth2ClientAuthorityRepository repository;

    /**
     * current class instance's member.
     * The Oauth 2 service.
     */
    @Autowired
    private Oauth2Service oauth2Service;

    /**
     * current class instance's member.
     * The Redis template.
     */
    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    /**
     * Find oauth 2 client authority by client id set.
     *
     * @param clientId the client id
     * @return the set
     */
    @Override
    @Cacheable(cacheNames = GATEWAY_AUTHORITY_CACHE, key = "#clientId")
    public Set<String> findOauth2ClientAuthorityByClientId(String clientId) {
        List<Oauth2ClientAuthority> authorities =
                repository.findOauth2ClientAuthoritiesByClientId(clientId);
        return authorities.stream().map(Oauth2ClientAuthority::getServiceName).collect(Collectors.toSet());
    }

    /**
     * Client info oauth 2 token info.
     *
     * @param token the token
     * @return the oauth 2 token info
     */
    @Override
    public Oauth2TokenInfo clientInfo(String token) {
        String key = CLIENT_TOKEN_INFO_CACHE + ":" + token;
        ValueOperations<String, String> operations = redisTemplate.opsForValue();
        String content = operations.get(key);

        if (StringUtils.isNotBlank(content)){
            return JsonUtils.jsonToObject(content, Oauth2TokenInfo.class);
        }

        JsonResult<Oauth2TokenInfo> result = oauth2Service.checkToken(token);
        Oauth2TokenInfo clientInfo = ResponseUtils.checkResult(result);

        long expireTime = clientInfo.getExp();
        operations.set(key, JsonUtils.objectToJson(clientInfo));
        redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
        return clientInfo;
    }
}
